import json
import random
import re
import subprocess
from functools import partial
from urllib.parse import quote
subprocess.Popen = partial(subprocess.Popen, encoding="utf-8")
import execjs
import requests
from pymongo import MongoClient
from lxml import html
from lxml import etree
import os
from django.conf import settings

class HuanghuaiSpider:
    def __init__(self):
        self.client = MongoClient("mongodb://localhost:27017")
        self.coll = self.client["huanghuai"]["majors"]
        self.session = requests.Session()
        self.headers = {
            "accept": "text/plain, */*; q=0.01",
            "accept-language": "zh-CN,zh;q=0.9,oc;q=0.8",
            "cache-control": "no-cache",
            "content-type": "application/x-www-form-urlencoded; charset=UTF-8",
            "origin": "https://jwglxt.huanghuai.edu.cn",
            "pragma": "no-cache",
            "priority": "u=1, i",
            "referer": "https://jwglxt.huanghuai.edu.cn/cas/login.action",
            "sec-ch-ua": "\"Not)A;Brand\";v=\"8\", \"Chromium\";v=\"138\", \"Google Chrome\";v=\"138\"",
            "sec-ch-ua-mobile": "?0",
            "sec-ch-ua-platform": "\"Windows\"",
            "sec-fetch-dest": "empty",
            "sec-fetch-mode": "cors",
            "sec-fetch-site": "same-origin",
            "user-agent": "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/138.0.0.0 Safari/537.36",
            "x-requested-with": "XMLHttpRequest"
        }

    def get_param(self):
        """获取登录参数"""
        url = 'https://jwglxt.huanghuai.edu.cn/custom/js/SetKingoEncypt.jsp'
        headers = {
            "Accept": "*/*",
            "Accept-Language": "zh-CN,zh;q=0.9,oc;q=0.8",
            "Cache-Control": "no-cache",
            "Connection": "keep-alive",
            "Pragma": "no-cache",
            "Referer": "https://jwglxt.huanghuai.edu.cn/cas/login.action",
            "Sec-Fetch-Dest": "script",
            "Sec-Fetch-Mode": "no-cors",
            "Sec-Fetch-Site": "same-origin",
            "User-Agent": "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/138.0.0.0 Safari/537.36",
            "sec-ch-ua-mobile": "?0"
        }

        params = {
            "random": "0.2829450012443646"
        }
        
        try:
            response = self.session.get(url, headers=headers, params=params, timeout=10)
            if response.status_code != 200:
                print(f"获取参数失败，状态码: {response.status_code}")
                return None
                
            pp = response.text.split("'")
            if len(pp) < 6:
                print(f"参数解析失败，响应内容: {response.text[:200]}")
                return None
                
            param = {}
            param["_deskey"] = pp[1]
            param["_nowtime"] = pp[3]
            param["_ssessionid"] = pp[5]
            return param
        except Exception as e:
            print(f"获取登录参数异常: {e}")
            return None

    def login(self, username, password):
        """登录教务系统"""
        url = "https://jwglxt.huanghuai.edu.cn/cas/logon.action"
        pp = self.get_param()
        print(f"pp-> {pp}")
        if pp is None:
            print("获取登录参数失败")
            return False
        
        # 获取加密脚本路径
        js_file_path = os.path.join(settings.BASE_DIR, 'jwxt', 'jiami.js')
        
        try:
            params = execjs.compile(open(js_file_path, 'r', encoding='utf-8').read()).call(('set_login'),pp['_deskey'],pp['_nowtime'],pp['_ssessionid'],password,username)
            token = execjs.compile(open(js_file_path, 'r', encoding='utf-8').read()).call(('set_token'),username,password,pp["_nowtime"],pp['_ssessionid'])
            
            print(f"登录参数: {params}")
            print(f"登录令牌: {token}")
            
            data = {
                "params": params,
                "token": token,
                "timestamp": pp['_nowtime'],
                "deskey": pp['_deskey'],
                "ssessionid": pp['_ssessionid']
            }
            
            response = self.session.post(url, data=data, headers=self.headers, timeout=10)
            print(f"登录响应状态码: {response.status_code}")
            print(f"登录响应内容: {response.text[:200]}")
            
            # 检查登录是否成功（可能需要根据实际响应调整判断逻辑）
            if response.status_code == 200:
                # 检查响应中是否包含成功标识
                if "success" in response.text.lower() or "成功" in response.text:
                    return True
                elif "error" in response.text.lower() or "错误" in response.text or "失败" in response.text:
                    print("登录失败：用户名或密码错误")
                    return False
                else:
                    # 如果没有明确的成功/失败标识，假设状态码200表示成功
                    return True
            else:
                return False
                
        except Exception as e:
            print(f"登录失败: {e}")
            return False

    def get_course_params(self, major_name):
        """获取课程参数"""
        result = []
        url = "https://jwglxt.huanghuai.edu.cn/custom/js/SetRootPath.jsp"
        
        try:
            response = self.session.get(url, headers=self.headers, timeout=10)
            if response.status_code != 200:
                print(f"获取课程参数失败，状态码: {response.status_code}")
                return result
            
            _xsbjdm = re.findall(r"_xsbjdm\s*=\s*'([^']+)'", response.text)
            _xsbjyhdm = re.findall(r"_xsbjyhdm\s*=\s*'([^']+)'", response.text)
            _xsbjmc = re.findall(r"_xsbjmc\s*=\s*'([^']+)'", response.text)
            _selBJMC=f"[{_xsbjmc[0]}]{_xsbjyhdm[0]}"
            encoded = quote(_selBJMC, safe='', encoding='gbk')
            print(f"_selBJMC-> {_selBJMC}")
            if _xsbjdm and _xsbjyhdm and _xsbjmc:
                result.append(_xsbjdm[0])
                result.append(_xsbjyhdm[0])
                result.append(encoded)
                # 处理专业名称
                m = major_name
                for i in range(0, len(m)):
                    if m[i] >= '0' and m[i] <= '9':
                        m = m[:i]
                        break
                
                # 查询专业代码
                try:
                    clauses = [{"name": re.compile(c, re.IGNORECASE)} for c in m]
                    query = {"$and": clauses} if len(clauses) > 1 else clauses[0]
                    
                    for doc in self.coll.find(query):
                        result.append(doc['code'])
                        break
                    
                    # 如果没有找到专业代码，使用默认值
                    if len(result) < 3:
                        print(f"未找到专业 {major_name} 的代码，使用默认值")
                        result.append("4010")  # 默认专业代码
                        
                except Exception as e:
                    print(f"查询专业代码失败: {e}")
                    result.append("4010")  # 默认专业代码
            else:
                print("未能解析到学生参数")
                
        except Exception as e:
            print(f"获取课程参数异常: {e}")
            
        return result

    def extract_section_from_course_text(self, course_text):
        """从课程文本中提取节次信息"""
        import re
        
        # 课程文本格式：数字逻辑&ensp;考查&ensp;王晓涓&ensp;[1-16]周&ensp;1-2节&ensp;010409
        # 使用正则表达式提取节次信息
        section_pattern = r'(\d+(?:-\d+)?节)'
        match = re.search(section_pattern, course_text)
        
        if match:
            time_range = match.group(1)  # 如 "1-2节"
            
            # 解析节次范围，确定具体的节次
            if '-' in time_range:
                start_section, end_section = time_range.replace('节', '').split('-')
                start_section = int(start_section)
                end_section = int(end_section)
            else:
                start_section = end_section = int(time_range.replace('节', ''))
            
            # 根据节次范围确定时间段和具体节次
            section_info = self.get_section_info(start_section, end_section)
            
            return {
                'section': section_info['section'],
                'time_range': time_range,
                'start_section': start_section,
                'end_section': end_section
            }
        else:
            # 如果没有找到节次信息，返回默认值
            return {
                'section': '未知',
                'time_range': '未知',
                'start_section': 0,
                'end_section': 0
            }
    
    def get_section_info(self, start_section, end_section):
        """根据节次数字确定时间段和节次描述"""
        # 大学课程表的标准规律（一天5节大课）：
        # 1-2节：上午第一节 -> '一'
        # 3-4节：上午第二节 -> '二'
        # 5-6节：下午第三节 -> '三'
        # 7-8节：下午第四节 -> '四'
        # 9-10节：晚上第五节 -> '五'
        
        if start_section <= 2:
            period = "上午"
            section = "一"  # 第一节大课
        elif start_section <= 4:
            period = "上午"
            section = "二"  # 第二节大课
        elif start_section <= 6:
            period = "下午"
            section = "三"  # 第三节大课
        elif start_section <= 8:
            period = "下午"
            section = "四"  # 第四节大课
        else:
            period = "晚上"
            section = "五"  # 第五节大课
        
        return {
            'period': period,
            'section': section
        }

    def get_course_schedule(self, major_name):
        """获取课程表"""
        result = self.get_course_params(major_name)
        if len(result) < 3:
            return []
            
        url = "https://jwglxt.huanghuai.edu.cn/kbbp/dykb.GS1.jsp"
        params = {
            "kblx": "bjkb"
        }
        data = {
            "hidNJ": "",
            "hidBJDM": f"{result[0]}",
            "hidXQ": "2",
            "hidPYCC": "",
            "userType": "STU",
            "hidYXB": f"{result[3][0:2]}",
            "hidBfy": "0",
            "hidZYDM": f"{result[3]}",
            "hidCXLX": "fbj",
            "hidZZLX": "A4",
            "orientation": "L",
            "xssj": "xssj",
            "xsrq": "xsrq",
            "xsxxnr": "1",
            "hidFlag": "",
            "menucode": "SB03",
            "initQry": "0",
            "xnxq": "2025-0",
            "xn": "2025",
            "xn1": "",
            "_xq": "",
            "xq_m": "0",
            "sfxsym": "xsym",
            "jslx": "",
            "isNjQuery": "on",
            "nj": "2023",
            "selXQ": "2",
            "selYXB": f"{result[3][0:2]}",
            "chkXsxxnr": "on",
            "selPYCC": "",
            "selZY": f"{result[3]}",
            "selBJMC": f"{result[2]}",
            "selBJ": f"{result[0]}",
            "radiob": "A4",
            "radiofx": "hx",
            "selGS": "1",
            "radioa": "5",
            "chkXSDYRQ": "on",
            "chkXSDYSJ": "on",
            "chkXSYM": "on",
            "menucode_current": "SB03"
        }
        
        response = self.session.post(url, headers=self.headers, params=params, data=data)
        #日志输出
        print(response.text)
        try:
            doc = html.fromstring(response.text)
            table = doc.xpath('//*[@id="mytable0"]')
            
            weekday = ['星期一', '星期二', '星期三', '星期四', '星期五', '星期六', '星期日']
            lessons = []
            
            # table.xpath() 返回的是列表，需要先获取第一个元素
            if not table:
                print("未找到课程表")
                return []
            
            table_element = table[0]  # 获取第一个table元素
            tbody_elements = table_element.xpath('./tbody')
            
            current_period = ""  # 当前时间段（上午/下午/晚上）
            
            # 遍历所有tbody，然后遍历每个tbody中的tr
            for tbody in tbody_elements:
                rows = tbody.xpath('./tr')
                
                for tr in rows:
                    # 获取当前行的所有td1元素
                    td1_elements = tr.xpath('./td[@class="td1"]')
                    
                    # 判断是否有时间段信息（上午/下午/晚上）
                    for td1 in td1_elements:
                        td1_text = ''.join(td1.xpath('.//text()')).strip()
                        if td1_text in ['上午', '下午', '晚上']:
                            current_period = td1_text
                            break
                    
                    # 获取课程内容（排除td1类的列，这些是时间段和节次列）
                    course_tds = tr.xpath('./td[not(@class="td1")]')
                    
                    for col, td in enumerate(course_tds, start=1):
                        # 确保不超过7天（星期一到星期日）
                        if col > len(weekday):
                            break
                            
                        div1 = td.xpath('.//div[@class="div1"]')
                        if not div1:
                            continue
                        txt = ''.join(div1[0].xpath('.//text()')).strip()
                        if not txt:
                            continue
                        
                        # 从课程文本中提取节次信息
                        section_info = self.extract_section_from_course_text(txt)
                        
                        # 使用从课程文本中提取的真实时间段，而不是HTML表格的时间段
                        real_section_info = self.get_section_info(section_info['start_section'], section_info['end_section'])
                        
                        lessons.append({
                            'weekday': weekday[col-1],
                            'period': real_section_info['period'],  # 使用从节次推算的真实时间段
                            'section': real_section_info['section'],  # 前端期望的简单节次（一、二、三、四、五）
                            'time_range': section_info['time_range'],  # 节次范围（如"1-2节"）
                            'start_section': section_info['start_section'],  # 开始节次
                            'end_section': section_info['end_section'],  # 结束节次
                            'course': txt.replace('&ensp;', ' ')
                        })
            
            print(f"成功解析到 {len(lessons)} 门课程")
            
            # 添加一个示例输出来检查数据结构
            if lessons:
                print("示例课程数据:")
                print(lessons[0])
            
            return lessons
        except Exception as e:
            print(f"解析课程表失败: {e}")
            return []

    def get_grades(self):
        """获取成绩信息"""
        data = {
            "sjxz": "sjxz3",
            "ysyx": "yscj",
            "zx": "1",
            "fx": "1",
            "rxnj": "2023",
            "btnExport": "%B5%BC%B3%F6",
            "xn": "2024",
            "xn1": "2025",
            "xq": "1",
            "ysyxS": "on",
            "sjxzS": "on",
            "zxC": "on",
            "fxC": "on",
            "xsjd": "1",
            "menucode_current": "S40303"
        }
        
        headers = {
            "Accept": "*/*",
            "Accept-Language": "zh-CN,zh;q=0.9,oc;q=0.8",
            "Cache-Control": "no-cache",
            "Connection": "keep-alive",
            "Content-Type": "application/x-www-form-urlencoded",
            "Origin": "https://jwglxt.huanghuai.edu.cn",
            "Pragma": "no-cache",
            "Referer": "https://jwglxt.huanghuai.edu.cn/student/xscj.stuckcj_data.jsp",
            "Sec-Fetch-Dest": "script",
            "Sec-Fetch-Mode": "no-cors",
            "Sec-Fetch-Site": "same-origin",
            "Sec-Fetch-User": "?1",
            "Upgrade-Insecure-Requests": "1",
            "User-Agent": "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/138.0.0.0 Safari/537.36",
            "sec-ch-ua-mobile": "?0"
        }
        
        resp = self.session.post(
            url='https://jwglxt.huanghuai.edu.cn/student/xscj.stuckcj_data.jsp',
            headers=headers,
            data=data
        )
        
        try:
            html_tree = etree.HTML(resp.text)
            信息 = html_tree.xpath('/html/body/div[2]/div/text()')
            头部 = html_tree.xpath('/html/body/table[2]/thead/tr/td/text()')
            成绩 = html_tree.xpath('/html/body/table[2]/tbody/tr')
            
            grades_data = {
                'info': [info.strip() for info in 信息 if info.strip()],
                'headers': [header.strip() for header in 头部 if header.strip()],
                'grades': []
            }
            
            for row in 成绩:
                des = row.xpath('.//td/text()')
                if len(des) >= 2:
                    subject = des[1].strip() if len(des) > 1 else ''
                    grade = des[-1].strip() if des else ''
                    if subject and grade:
                        grades_data['grades'].append({
                            'subject': subject,
                            'grade': grade
                        })
            
            return grades_data
        except Exception as e:
            print(f"解析成绩失败: {e}")
            return {'info': [], 'headers': [], 'grades': []}
